home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Games Extra 1996 September
/
Amiga Games Extra CD-ROM 9-1996.iso
/
userbox
/
publicdomain
/
vim-4.2
/
src
/
unix.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-16
|
61KB
|
2,883 lines
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
* OS/2 port by Paul Slootman
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...)
* Also for OS/2, using the excellent EMX package!!!
*
* A lot of this file was originally written by Juergen Weigert and later
* changed beyond recognition.
*/
/*
* Some systems have a prototype for select() that has (int *) instead of
* (fd_set *), which is wrong. This define removes that prototype. We include
* our own prototype in osdef.h.
*/
#define select select_declared_wrong
#include "vim.h"
#include "globals.h"
#include "option.h"
#include "proto.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include "unixunix.h" /* unix includes for unix.c only */
/*
* Use this prototype for select, some include files have a wrong prototype
*/
#undef select
#if defined(HAVE_SELECT)
extern int select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *));
#endif
/*
* end of autoconf section. To be extended...
*/
/* Are the following #ifdefs still required? And why? Is that for X11? */
#if defined(ESIX) || defined(M_UNIX) && !defined(SCO)
# ifdef SIGWINCH
# undef SIGWINCH
# endif
# ifdef TIOCGWINSZ
# undef TIOCGWINSZ
# endif
#endif
#if defined(SIGWINDOW) && !defined(SIGWINCH) /* hpux 9.01 has it */
# define SIGWINCH SIGWINDOW
#endif
#if defined(HAVE_X11) && defined(WANT_X11)
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Xatom.h>
Window x11_window = 0;
Display *x11_display = NULL;
int got_x_error = FALSE;
static int get_x11_windis __ARGS((void));
static void set_x11_title __ARGS((char_u *));
static void set_x11_icon __ARGS((char_u *));
#endif
static int get_x11_title __ARGS((int));
static int get_x11_icon __ARGS((int));
static void may_core_dump __ARGS((void));
static int Read __ARGS((char_u *, long));
static int WaitForChar __ARGS((long));
static int RealWaitForChar __ARGS((int, long));
static void fill_inbuf __ARGS((int));
#if defined(SIGWINCH)
static RETSIGTYPE sig_winch __ARGS(SIGPROTOARG);
#endif
#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
static RETSIGTYPE sig_alarm __ARGS(SIGPROTOARG);
#endif
static RETSIGTYPE deathtrap __ARGS(SIGPROTOARG);
static void catch_signals __ARGS((RETSIGTYPE (*func)()));
#ifndef __EMX__
static int have_wildcard __ARGS((int, char_u **));
static int have_dollars __ARGS((int, char_u **));
#endif
static int do_resize = FALSE;
static char_u *oldtitle = NULL;
static char_u *fixedtitle = (char_u *)"Thanks for flying Vim";
static char_u *oldicon = NULL;
#ifndef __EMX__
static char_u *extra_shell_arg = NULL;
static int show_shell_mess = TRUE;
#endif
static int core_dump = FALSE; /* core dump in mch_windexit() */
#ifdef SYS_SIGLIST_DECLARED
/*
* I have seen
* extern char *_sys_siglist[NSIG];
* on Irix, Linux, NetBSD and Solaris. It contains a nice list of strings
* that describe the signals. That is nearly what we want here. But
* autoconf does only check for sys_siglist (without the underscore), I
* do not want to change everything today.... jw.
* This is why AC_DECL_SYS_SIGLIST is commented out in configure.in
*/
#endif
static struct
{
int sig; /* Signal number, eg. SIGSEGV etc */
char *name; /* Signal name (not char_u!). */
int dump; /* Should this signal cause a core dump? */
} signal_info[] =
{
#ifdef SIGHUP
{SIGHUP, "HUP", FALSE},
#endif
#ifdef SIGINT
{SIGINT, "INT", FALSE},
#endif
#ifdef SIGQUIT
{SIGQUIT, "QUIT", TRUE},
#endif
#ifdef SIGILL
{SIGILL, "ILL", TRUE},
#endif
#ifdef SIGTRAP
{SIGTRAP, "TRAP", TRUE},
#endif
#ifdef SIGABRT
{SIGABRT, "ABRT", TRUE},
#endif
#ifdef SIGEMT
{SIGEMT, "EMT", TRUE},
#endif
#ifdef SIGFPE
{SIGFPE, "FPE", TRUE},
#endif
#ifdef SIGBUS
{SIGBUS, "BUS", TRUE},
#endif
#ifdef SIGSEGV
{SIGSEGV, "SEGV", TRUE},
#endif
#ifdef SIGSYS
{SIGSYS, "SYS", TRUE},
#endif
#ifdef SIGALRM
{SIGALRM, "ALRM", FALSE},
#endif
#ifdef SIGTERM
{SIGTERM, "TERM", FALSE},
#endif
#ifdef SIGVTALRM
{SIGVTALRM, "VTALRM", FALSE},
#endif
#ifdef SIGPROF
{SIGPROF, "PROF", FALSE},
#endif
#ifdef SIGXCPU
{SIGXCPU, "XCPU", TRUE},
#endif
#ifdef SIGXFSZ
{SIGXFSZ, "XFSZ", TRUE},
#endif
#ifdef SIGUSR1
{SIGUSR1, "USR1", FALSE},
#endif
#ifdef SIGUSR2
{SIGUSR2, "USR2", FALSE},
#endif
{-1, "Unknown!", -1}
};
void
mch_write(s, len)
char_u *s;
int len;
{
#ifdef USE_GUI
if (gui.in_use && !gui.dying)
{
gui_write(s, len);
if (p_wd)
gui_mch_wait_for_chars(p_wd);
}
else
#endif
{
write(1, (char *)s, len);
if (p_wd) /* Unix is too fast, slow down a bit more */
RealWaitForChar(0, p_wd);
}
}
/*
* mch_inchar(): low level input funcion.
* Get a characters from the keyboard.
* Return the number of characters that are available.
* If wtime == 0 do not wait for characters.
* If wtime == n wait a short time for characters.
* If wtime == -1 wait forever for characters.
*/
int
mch_inchar(buf, maxlen, wtime)
char_u *buf;
int maxlen;
long wtime; /* don't use "time", MIPS cannot handle it */
{
int len;
#ifdef USE_GUI
if (gui.in_use)
{
if (!gui_mch_wait_for_chars(wtime))
return 0;
return Read(buf, (long)maxlen);
}
#endif
if (wtime >= 0)
{
while (WaitForChar(wtime) == 0) /* no character available */
{
if (!do_resize) /* return if not interrupted by resize */
return 0;
set_winsize(0, 0, FALSE);
do_resize = FALSE;
}
}
else /* wtime == -1 */
{
/*
* If there is no character available within 'updatetime' seconds
* flush all the swap files to disk
* Also done when interrupted by SIGWINCH.
*/
if (WaitForChar(p_ut) == 0)
updatescript(0);
}
for (;;) /* repeat until we got a character */
{
if (do_resize) /* window changed size */
{
set_winsize(0, 0, FALSE);
do_resize = FALSE;
}
/*
* we want to be interrupted by the winch signal
*/
WaitForChar(-1L);
if (do_resize) /* interrupted by SIGWINCHsignal */
continue;
/*
* For some terminals we only get one character at a time.
* We want the get all available characters, so we could keep on
* trying until none is available
* For some other terminals this is quite slow, that's why we don't do
* it.
*/
len = Read(buf, (long)maxlen);
if (len > 0)
{
#ifdef OS2
int i;
for (i = 0; i < len; i++)
if (buf[i] == 0)
buf[i] = K_NUL;
#endif
return len;
}
}
}
/*
* return non-zero if a character is available
*/
int
mch_char_avail()
{
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_update();
return !is_input_buf_empty();
}
#endif
return WaitForChar(0L);
}
long
mch_avail_mem(special)
int special;
{
#ifdef __EMX__
return ulimit(3, 0L); /* always 32MB? */
#else
return 0x7fffffff; /* virtual memory eh */
#endif
}
void
mch_delay(msec, ignoreinput)
long msec;
int ignoreinput;
{
if (ignoreinput)
#ifndef HAVE_SELECT
poll(NULL, 0, (int)msec);
#else
# ifdef __EMX__
_sleep2(msec);
# else
{
struct timeval tv;
tv.tv_sec = msec / 1000;
tv.tv_usec = (msec % 1000) * 1000;
select(0, NULL, NULL, NULL, &tv);
}
# endif /* __EMX__ */
#endif /* HAVE_SELECT */
else
#ifdef USE_GUI
if (gui.in_use)
gui_mch_wait_for_chars(msec);
else
#endif
WaitForChar(msec);
}
#if defined(SIGWINCH)
/*
* We need correct potatotypes, otherwise mean compilers will barf when the
* second argument to signal() is ``wrong''.
* Let me try it with a few tricky defines from my own osdef.h (jw).
*/
static RETSIGTYPE
sig_winch SIGDEFARG(sigarg)
{
/* this is not required on all systems, but it doesn't hurt anybody */
signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
do_resize = TRUE;
SIGRETURN;
}
#endif
#if defined(SIGALRM) && defined(HAVE_X11) && defined(WANT_X11)
/*
* signal function for alarm().
*/
static RETSIGTYPE
sig_alarm SIGDEFARG(sigarg)
{
/* doesn't do anything, just to break a system call */
SIGRETURN;
}
#endif
void
mch_resize()
{
do_resize = TRUE;
}
/*
* This function handles deadly signals.
* It tries to preserve any swap file and exit properly.
* (partly from Elvis).
*/
static RETSIGTYPE
deathtrap SIGDEFARG(sigarg)
{
static int entered = 0;
#ifdef SIGHASARG
int i;
for (i = 0; signal_info[i].dump != -1; i++)
{
if (sigarg == signal_info[i].sig)
{
if (signal_info[i].dump)
core_dump = TRUE;
break;
}
}
#endif
/*
* If something goes wrong after entering here, we may get here again.
* When this happens, give a message and try to exit nicely (resetting the
* terminal mode, etc.)
* When this happens twice, just exit, don't even try to give a message,
* stack may be corrupt or something weird.
*/
if (entered == 2)
{
may_core_dump();
exit(7);
}
if (entered)
{
OUTSTR("Vim: Double signal, exiting\n");
flushbuf();
getout(1);
}
++entered;
sprintf((char *)IObuff, "Vim: Caught %s %s\n",
#ifdef SIGHASARG
"deadly signal", signal_info[i].name);
#else
"some", "deadly signal");
#endif
preserve_exit(); /* preserve files and exit */
SIGRETURN;
}
/*
* If the machine has job control, use it to suspend the program,
* otherwise fake it by starting a new shell.
* When running the GUI iconify the window.
*/
void
mch_suspend()
{
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_iconify();
return;
}
#endif
#ifdef SIGTSTP
flushbuf(); /* needed to make cursor visible on some systems */
settmode(0);
flushbuf(); /* needed to disable mouse on some systems */
kill(0, SIGTSTP); /* send ourselves a STOP signal */
/*
* Set oldtitle to NULL, so the current title is obtained again.
*/
if (oldtitle != fixedtitle)
{
vim_free(oldtitle);
oldtitle = NULL;
}
settmode(1);
#else
MSG_OUTSTR("new shell started\n");
(void)call_shell(NULL, SHELL_COOKED);
#endif
need_check_timestamps = TRUE;
}
void
mch_windinit()
{
Columns = 80;
Rows = 24;
flushbuf();
(void)mch_get_winsize();
#if defined(SIGWINCH)
/*
* WINDOW CHANGE signal is handled with sig_winch().
*/
signal(SIGWINCH, (RETSIGTYPE (*)())sig_winch);
#endif
/*
* We want the STOP signal to work, to make mch_suspend() work
*/
#ifdef SIGTSTP
signal(SIGTSTP, SIG_DFL);
#endif
/*
* We want to ignore breaking of PIPEs.
*/
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
/*
* Arrange for other signals to gracefully shutdown Vim.
*/
catch_signals(deathtrap);
}
static void
catch_signals(func)
RETSIGTYPE (*func)();
{
int i;
for (i = 0; signal_info[i].dump != -1; i++)
signal(signal_info[i].sig, func);
}
void
reset_signals()
{
catch_signals(SIG_DFL);
}
/*
* Check_win checks whether we have an interactive window.
*/
int
mch_check_win(argc, argv)
int argc;
char **argv;
{
if (isatty(1))
return OK;
return FAIL;
}
int
mch_check_input()
{
if (isatty(0))
return OK;
return FAIL;
}
#if defined(HAVE_X11) && defined(WANT_X11)
/*
* X Error handler, otherwise X just exits! (very rude) -- webb
*/
static int
x_error_handler(dpy, error_event)
Display *dpy;
XErrorEvent *error_event;
{
XGetErrorText(dpy, error_event->error_code, (char *)IObuff, IOSIZE);
STRCAT(IObuff, "\nVim: Got X error\n");
#if 1
preserve_exit(); /* preserve files and exit */
#else
printf(IObuff); /* print error message and continue */
/* Makes my system hang */
#endif
return 0; /* NOTREACHED */
}
/*
* Another X Error handler, just used to check for errors.
*/
static int
x_error_check(dpy, error_event)
Display *dpy;
XErrorEvent *error_event;
{
got_x_error = TRUE;
return 0;
}
/*
* try to get x11 window and display
*
* return FAIL for failure, OK otherwise
*/
static int
get_x11_windis()
{
char *winid;
XTextProperty text_prop;
int (*old_handler)();
static int result = -1;
static int x11_display_opened_here = FALSE;
/* X just exits if it finds an error otherwise! */
XSetErrorHandler(x_error_handler);
#ifdef USE_GUI_X11
if (gui.in_use)
{
/*
* If the X11 display was opened here before, for the window where Vim
* was started, close that one now to avoid a memory leak.
*/
if (x11_display_opened_here && x11_display != NULL)
{
XCloseDisplay(x11_display);
x11_display = NULL;
x11_display_opened_here = FALSE;
}
return gui_get_x11_windis(&x11_window, &x11_display);
}
#endif
if (result != -1) /* Have already been here and set this */
return result; /* Don't do all these X calls again */
/*
* If WINDOWID not set, should try another method to find out
* what the current window number is. The only code I know for
* this is very complicated.
* We assume that zero is invalid for WINDOWID.
*/
if (x11_window == 0 && (winid = getenv("WINDOWID")) != NULL)
x11_window = (Window)atol(winid);
if (x11_window != 0 && x11_display == NULL)
{
#ifdef SIGALRM
RETSIGTYPE (*sig_save)();
/*
* Opening the Display may hang if the DISPLAY setting is wrong, or
* the network connection is bad. Set an alarm timer to get out.
*/
sig_save = (RETSIGTYPE (*)())signal(SIGALRM,
(RETSIGTYPE (*)())sig_alarm);
alarm(2);
#endif
x11_display = XOpenDisplay(NULL);
#ifdef SIGALRM
alarm(0);
signal(SIGALRM, (RETSIGTYPE (*)())sig_save);
#endif
if (x11_display != NULL)
{
/*
* Try to get the window title. I don't actually want it yet, so
* there may be a simpler call to use, but this will cause the
* error handler x_error_check() to be called if anything is wrong,
* such as the window pointer being invalid (as can happen when the
* user changes his DISPLAY, but not his WINDOWID) -- webb
*/
old_handler = XSetErrorHandler(x_error_check);
got_x_error = FALSE;
if (XGetWMName(x11_display, x11_window, &text_prop))
XFree((void *)text_prop.value);
XSync(x11_display, False);
if (got_x_error)
{
/* Maybe window id is bad */
x11_window = 0;
XCloseDisplay(x11_display);
x11_display = NULL;
}
else
x11_display_opened_here = TRUE;
XSetErrorHandler(old_handler);
}
}
if (x11_window == 0 || x11_display == NULL)
return (result = FAIL);
return (result = OK);
}
/*
* Determine original x11 Window Title
*/
static int
get_x11_title(test_only)
int test_only;
{
XTextProperty text_prop;
int retval = FALSE;
if (get_x11_windis() == OK)
{
/* Get window name if any */
if (XGetWMName(x11_display, x11_window, &text_prop))
{
if (text_prop.value != NULL)
{
retval = TRUE;
if (!test_only)
oldtitle = strsave((char_u *)text_prop.value);
}
XFree((void *)text_prop.value);
}
}
if (oldtitle == NULL && !test_only) /* could not get old title */
oldtitle = fixedtitle;
return retval;
}
/*
* Determine original x11 Window icon
*/
static int
get_x11_icon(test_only)
int test_only;
{
XTextProperty text_prop;
int retval = FALSE;
if (get_x11_windis() == OK)
{
/* Get icon name if any */
if (XGetWMIconName(x11_display, x11_window, &text_prop))
{
if (text_prop.value != NULL)
{
retval = TRUE;
if (!test_only)
oldicon = strsave((char_u *)text_prop.value);
}
XFree((void *)text_prop.value);
}
}
/* could not get old icon, use terminal name */
if (oldicon == NULL && !test_only)
{
if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
oldicon = term_strings[KS_NAME] + 8;
else
oldicon = term_strings[KS_NAME];
}
return retval;
}
/*
* Set x11 Window Title
*
* get_x11_windis() must be called before this and have returned OK
*/
static void
set_x11_title(title)
char_u *title;
{
#if XtSpecificationRelease >= 4
XTextProperty text_prop;
text_prop.value = title;
text_prop.nitems = STRLEN(title);
text_prop.encoding = XA_STRING;
text_prop.format = 8;
XSetWMName(x11_display, x11_window, &text_prop);
#else
XStoreName(x11_display, x11_window, (char *)title);
#endif
XFlush(x11_display);
}
/*
* Set x11 Window icon
*
* get_x11_windis() must be called before this and have returned OK
*/
static void
set_x11_icon(icon)
char_u *icon;
{
#if XtSpecificationRelease >= 4
XTextProperty text_prop;
text_prop.value = icon;
text_prop.nitems = STRLEN(icon);
text_prop.encoding = XA_STRING;
text_prop.format = 8;
XSetWMIconName(x11_display, x11_window, &text_prop);
#else
XSetIconName(x11_display, x11_window, (char *)icon);
#endif
XFlush(x11_display);
}
#else /* HAVE_X11 && WANT_X11 */
static int
get_x11_title(test_only)
int test_only;
{
if (!test_only)
oldtitle = fixedtitle;
return FALSE;
}
static int
get_x11_icon(test_only)
int test_only;
{
if (!test_only)
{
if (STRNCMP(term_strings[KS_NAME], "builtin_", 8) == 0)
oldicon = term_strings[KS_NAME] + 8;
else
oldicon = term_strings[KS_NAME];
}
return FALSE;
}
#endif /* HAVE_X11 && WANT_X11 */
int
mch_can_restore_title()
{
#ifdef USE_GUI
/*
* If GUI is (going to be) used, we can always set the window title.
* Saves a bit of time, because the X11 display server does not need to be
* contacted.
*/
if (gui.starting || gui.in_use)
return TRUE;
#endif
return get_x11_title(TRUE);
}
int
mch_can_restore_icon()
{
#ifdef USE_GUI
/*
* If GUI is (going to be) used, we can always set the icon name.
* Saves a bit of time, because the X11 display server does not need to be
* contacted.
*/
if (gui.starting || gui.in_use)
return TRUE;
#endif
return get_x11_icon(TRUE);
}
/*
* Set the window title and icon.
* Currently only works for x11.
*/
void
mch_settitle(title, icon)
char_u *title;
char_u *icon;
{
int type = 0;
if (term_strings[KS_NAME] == NULL) /* no terminal name (yet) */
return;
if (title == NULL && icon == NULL) /* nothing to do */
return;
/*
* if the window ID and the display is known, we may use X11 calls
*/
#if defined(HAVE_X11) && defined(WANT_X11)
if (get_x11_windis() == OK)
type = 1;
#endif
/*
* Note: if terminal is xterm, title is set with escape sequence rather
* than x11 calls, because the x11 calls don't always work
* Check only if the start of the terminal name is "xterm", also catch
* "xterms".
*/
if (is_xterm(term_strings[KS_NAME]))
type = 2;
if (is_iris_ansi(term_strings[KS_NAME]))
type = 3;
if (type)
{
if (title != NULL)
{
if (oldtitle == NULL) /* first call, save title */
(void)get_x11_title(FALSE);
switch(type)
{
#if defined(HAVE_X11) && defined(WANT_X11)
case 1: set_x11_title(title); /* x11 */
break;
#endif
case 2: outstrn((char_u *)"\033]2;"); /* xterm */
outstrn(title);
outchar(Ctrl('G'));
flushbuf();
break;
case 3: outstrn((char_u *)"\033P1.y"); /* iris-ansi */
outstrn(title);
outstrn((char_u *)"\234");
flushbuf();
break;
}
}
if (icon != NULL)
{
if (oldicon == NULL) /* first call, save icon */
get_x11_icon(FALSE);
switch(type)
{
#if defined(HAVE_X11) && defined(WANT_X11)
case 1: set_x11_icon(icon); /* x11 */
break;
#endif
case 2: outstrn((char_u *)"\033]1;"); /* xterm */
outstrn(icon);
outchar(Ctrl('G'));
flushbuf();
break;
case 3: outstrn((char_u *)"\033P3.y"); /* iris-ansi */
outstrn(icon);
outstrn((char_u *)"\234");
flushbuf();
break;
}
}
}
}
int
is_xterm(name)
char_u *name;
{
if (name == NULL)
return FALSE;
return (vim_strnicmp(name, (char_u *)"xterm", (size_t)5) == 0 ||
STRCMP(name, "builtin_xterm") == 0);
}
int
is_iris_ansi(name)
char_u *name;
{
if (name == NULL)
return FALSE;
return (vim_strnicmp(name, (char_u *)"iris-ansi", (size_t)9) == 0 ||
STRCMP(name, "builtin_iris-ansi") == 0);
}
/*
* Return TRUE if "name" is a terminal for which 'ttyfast' should be set.
* This should include all windowed terminal emulators.
*/
int
is_fastterm(name)
char_u *name;
{
if (name == NULL)
return FALSE;
if (is_xterm(name) || is_iris_ansi(name))
return TRUE;
return (vim_strnicmp(name, (char_u *)"hpterm", (size_t)6) == 0 ||
vim_strnicmp(name, (char_u *)"sun-cmd", (size_t)7) == 0 ||
vim_strnicmp(name, (char_u *)"screen", (size_t)6) == 0 ||
vim_strnicmp(name, (char_u *)"dtterm", (size_t)6) == 0);
}
/*
* Restore the window/icon title.
* which is one of:
* 1 Just restore title
* 2 Just restore icon
* 3 Restore title and icon
*/
void
mch_restore_title(which)
int which;
{
mch_settitle((which & 1) ? oldtitle : NULL, (which & 2) ? oldicon : NULL);
}
/*
* Insert user name in s[len].
* Return OK if a name found.
*/
int
mch_get_user_name(s, len)
char_u *s;
int len;
{
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
struct passwd *pw;
#endif
uid_t uid;
uid = getuid();
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
if ((pw = getpwuid(uid)) != NULL &&
pw->pw_name != NULL && *pw->pw_name != NUL)
{
STRNCPY(s, pw->pw_name, len);
return OK;
}
#endif
sprintf((char *)s, "%d", (int)uid); /* assumes s is long enough */
return FAIL; /* a number is not a name */
}
/*
* Insert host name is s[len].
*/
#ifdef HAVE_SYS_UTSNAME_H
void
mch_get_host_name(s, len)
char_u *s;
int len;
{
struct utsname vutsname;
uname(&vutsname);
STRNCPY(s, vutsname.nodename, len);
}
#else /* HAVE_SYS_UTSNAME_H */
# ifdef HAVE_SYS_SYSTEMINFO_H
# define gethostname(nam, len) sysinfo(SI_HOSTNAME, nam, len)
# endif
void
mch_get_host_name(s, len)
char_u *s;
int len;
{
gethostname((char *)s, len);
}
#endif /* HAVE_SYS_UTSNAME_H */
/*
* return process ID
*/
long
mch_get_pid()
{
return (long)getpid();
}
#if !defined(HAVE_STRERROR) && defined(USE_GETCWD)
static char *strerror __ARGS((int));
static char *
strerror(err)
int err;
{
extern int sys_nerr;
extern char *sys_errlist[];
static char er[20];
if (err > 0 && err < sys_nerr)
return (sys_errlist[err]);
sprintf(er, "Error %d", err);
return er;
}
#endif
/*
* Get name of current directory into buffer 'buf' of length 'len' bytes.
* Return OK for success, FAIL for failure.
*/
int
mch_dirname(buf, len)
char_u *buf;
int len;
{
#if defined(USE_GETCWD)
if (getcwd((char *)buf, len) == NULL)
{
STRCPY(buf, strerror(errno));
return FAIL;
}
return OK;
#else
return (getwd((char *)buf) != NULL ? OK : FAIL);
#endif
}
#ifdef __EMX__
/*
* Replace all slashes by backslashes.
*/
static void
slash_adjust(p)
char_u *p;
{
while (*p)
{
if (*p == '/')
*p = '\\';
++p;
}
}
#endif
/*
* Get absolute filename into buffer 'buf' of length 'len' bytes.
*
* return FAIL for failure, OK for success
*/
int
FullName(fname, buf, len, force)
char_u *fname, *buf;
int len;
int force; /* also expand when already absolute path name */
{
int l;
#ifdef OS2
int only_drive; /* only a drive letter is specified in file name */
#endif
#ifdef HAVE_FCHDIR
int fd = -1;
static int dont_fchdir = FALSE; /* TRUE when fchdir() doesn't work */
#endif
char_u olddir[MAXPATHL];
char_u *p;
char_u c;
int retval = OK;
if (fname == NULL) /* always fail */
{
*buf = NUL;
return FAIL;
}
*buf = 0;
if (force || !isFullName(fname)) /* if forced or not an absolute path */
{
/*
* If the file name has a path, change to that directory for a moment,
* and then do the getwd() (and get back to where we were).
* This will get the correct path name with "../" things.
*/
#ifdef OS2
only_drive = 0;
if (((p = vim_strrchr(fname, '/')) != NULL) ||
((p = vim_strrchr(fname, '\\')) != NULL) ||
(((p = vim_strchr(fname, ':')) != NULL) && ++only_drive))
#else
if ((p = vim_strrchr(fname, '/')) != NULL)
#endif
{
#ifdef HAVE_FCHDIR
/*
* Use fchdir() if possible, it's said to be faster and more
* reliable. But on SunOS 4 it might not work. Check this by
* doing a fchdir() right now.
*/
if (!dont_fchdir)
{
fd = open(".", O_RDONLY | O_EXTRA);
if (fd >= 0 && fchdir(fd) < 0)
{
close(fd);
fd = -1;
dont_fchdir = TRUE; /* don't try again */
}
}
#endif
if (
#ifdef HAVE_FCHDIR
fd < 0 &&
#endif
mch_dirname(olddir, MAXPATHL) == FAIL)
{
p = NULL; /* can't get current dir: don't chdir */
retval = FAIL;
}
else
{
#ifdef OS2
/*
* compensate for case where ':' from "D:" was the only
* path separator detected in the file name; the _next_
* character has to be removed, and then restored later.
*/
if (only_drive)
p++;
#endif
c = *p;
*p = NUL;
if (vim_chdir((char *)fname))
retval = FAIL;
else
fname = p + 1;
*p = c;
#ifdef OS2
if (only_drive)
{
p--;
if (retval != FAIL)
fname--;
}
#endif
}
}
if (mch_dirname(buf, len) == FAIL)
{
retval = FAIL;
*buf = NUL;
}
l = STRLEN(buf);
if (l && buf[l - 1] != '/')
STRCAT(buf, "/");
if (p != NULL)
{
#ifdef HAVE_FCHDIR
if (fd >= 0)
{
fchdir(fd);
close(fd);
}
else
#endif
vim_chdir((char *)olddir);
}
}
STRCAT(buf, fname);
#ifdef OS2
slash_adjust(buf);
#endif
return retval;
}
/*
* return TRUE is fname is an absolute path name
*/
int
isFullName(fname)
char_u *fname;
{
#ifdef __EMX__
return _fnisabs(fname);
#else
return (*fname == '/' || *fname == '~');
#endif
}
/*
* get file permissions for 'name'
*/
long
getperm(name)
char_u *name;
{
struct stat statb;
if (stat((char *)name, &statb))
return -1;
return statb.st_mode;
}
/*
* set file permission for 'name' to 'perm'
*
* return FAIL for failure, OK otherwise
*/
int
setperm(name, perm)
char_u *name;
int perm;
{
return (chmod((char *)name, (mode_t)perm) == 0 ? OK : FAIL);
}
/*
* return TRUE if "name" is a directory
* return FALSE if "name" is not a directory
* return FALSE for error
*/
int
mch_isdir(name)
char_u *name;
{
struct stat statb;
if (stat((char *)name, &statb))
return FALSE;
#ifdef _POSIX_SOURCE
return (S_ISDIR(statb.st_mode) ? TRUE : FALSE);
#else
return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
#endif
}
void
mch_windexit(r)
int r;
{
settmode(0);
exiting = TRUE;
mch_settitle(oldtitle, oldicon); /* restore xterm title */
stoptermcap();
flushbuf();
ml_close_all(TRUE); /* remove all memfiles */
may_core_dump();
exit(r);
}
static void
may_core_dump()
{
#ifdef SIGQUIT
signal(SIGQUIT, SIG_DFL);
if (core_dump)
kill(getpid(), SIGQUIT); /* force a core dump */
#endif
}
static int curr_tmode = 0; /* contains current raw/cooked mode (0 = cooked) */
void
mch_settmode(raw)
int raw;
{
static int first = TRUE;
/* Why is NeXT excluded here (and not in unixunix.h)? */
#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
/* for "new" tty systems */
# ifdef HAVE_TERMIOS_H
static struct termios told;
struct termios tnew;
# else
static struct termio told;
struct termio tnew;
# endif
# ifdef TIOCLGET
static unsigned long tty_local;
# endif
if (raw)
{
if (first)
{
first = FALSE;
# ifdef TIOCLGET
ioctl(0, TIOCLGET, &tty_local);
# endif
# if defined(HAVE_TERMIOS_H)
tcgetattr(0, &told);
# else
ioctl(0, TCGETA, &told);
# endif
}
tnew = told;
/*
* ICRNL enables typing ^V^M
*/
tnew.c_iflag &= ~ICRNL;
tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
# if defined(IEXTEN) && !defined(MINT)
| IEXTEN /* IEXTEN enables typing ^V on SOLARIS */
/* but it breaks function keys on MINT */
# endif
);
tnew.c_cc[VMIN] = 1; /* return after 1 char */
tnew.c_cc[VTIME] = 0; /* don't wait */
# if defined(HAVE_TERMIOS_H)
tcsetattr(0, TCSANOW, &tnew);
# else
ioctl(0, TCSETA, &tnew);
# endif
}
else
{
# if defined(HAVE_TERMIOS_H)
tcsetattr(0, TCSANOW, &told);
# else
ioctl(0, TCSETA, &told);
# endif
# ifdef TIOCLGET
ioctl(0, TIOCLSET, &tty_local);
# endif
}
#else
# ifndef TIOCSETN
# define TIOCSETN TIOCSETP /* for hpux 9.0 */
# endif
/* for "old" tty systems */
static struct sgttyb ttybold;
struct sgttyb ttybnew;
if (raw)
{
if (first)
{
first = FALSE;
ioctl(0, TIOCGETP, &ttybold);
}
ttybnew = ttybold;
ttybnew.sg_flags &= ~(CRMOD | ECHO);
ttybnew.sg_flags |= RAW;
ioctl(0, TIOCSETN, &ttybnew);
}
else
ioctl(0, TIOCSETN, &ttybold);
#endif
curr_tmode = raw;
}
/*
* Try to get the code for "t_kb" from the stty setting
*
* Even if termcap claims a backspace key, the user's setting *should*
* prevail. stty knows more about reality than termcap does, and if
* somebody's usual erase key is DEL (which, for most BSD users, it will
* be), they're going to get really annoyed if their erase key starts
* doing forward deletes for no reason. (Eric Fischer)
*/
void
get_stty()
{
char_u buf[2];
char_u *p;
/* Why is NeXT excluded here (and not in unixunix.h)? */
#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)) && !defined(__NeXT__)
/* for "new" tty systems */
# ifdef HAVE_TERMIOS_H
struct termios keys;
# else
struct termio keys;
# endif
# if defined(HAVE_TERMIOS_H)
if (tcgetattr(0, &keys) != -1)
# else
if (ioctl(0, TCGETA, &keys) != -1)
# endif
{
buf[0] = keys.c_cc[VERASE];
#else
/* for "old" tty systems */
struct sgttyb keys;
if (ioctl(0, TIOCGETP, &keys) != -1)
{
buf[0] = keys.sg_erase;
#endif
buf[1] = NUL;
add_termcode((char_u *)"kb", buf);
/*
* If <BS> and <DEL> are now the same, redefine <DEL>.
*/
p = find_termcode((char_u *)"kD");
if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
do_fixdel();
}
#if 0
} /* to keep cindent happy */
#endif
}
#ifdef USE_MOUSE
/*
* set mouse clicks on or off (only works for xterms)
*/
void
mch_setmouse(on)
int on;
{
static int ison = FALSE;
if (on == ison) /* return quickly if nothing to do */
return;
if (is_xterm(term_strings[KS_NAME]))
{
if (on)
outstrn((char_u *)"\033[?1000h"); /* xterm: enable mouse events */
else
outstrn((char_u *)"\033[?1000l"); /* xterm: disable mouse events */
}
ison = on;
}
#endif
/*
* set screen mode, always fails.
*/
int
mch_screenmode(arg)
char_u *arg;
{
EMSG("Screen mode setting not supported");
return FAIL;
}
/*
* Try to get the current window size:
* 1. with an ioctl(), most accurate method
* 2. from the environment variables LINES and COLUMNS
* 3. from the termcap
* 4. keep using the old values
*/
int
mch_get_winsize()
{
int old_Rows = Rows;
int old_Columns = Columns;
char_u *p;
#ifdef USE_GUI
if (gui.in_use)
return gui_mch_get_winsize();
#endif
Columns = 0;
Rows = 0;
/*
* For OS/2 use _scrsize().
*/
# ifdef __EMX__
{
int s[2];
_scrsize(s);
Columns = s[0];
Rows = s[1];
}
# endif
/*
* 1. try using an ioctl. It is the most accurate method.
*
* Try using TIOCGWINSZ first, some systems that have it also define TIOCGSIZE
* but don't have a struct ttysize.
*/
# ifdef TIOCGWINSZ
{
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) == 0)
{
Columns = ws.ws_col;
Rows = ws.ws_row;
}
}
# else /* TIOCGWINSZ */
# ifdef TIOCGSIZE
{
struct ttysize ts;
if (ioctl(0, TIOCGSIZE, &ts) == 0)
{
Columns = ts.ts_cols;
Rows = ts.ts_lines;
}
}
# endif /* TIOCGSIZE */
# endif /* TIOCGWINSZ */
/*
* 2. get size from environment
*/
if (Columns == 0 || Rows == 0)
{
if ((p = (char_u *)getenv("LINES")))
Rows = atoi((char *)p);
if ((p = (char_u *)getenv("COLUMNS")))
Columns = atoi((char *)p);
}
#ifdef HAVE_TGETENT
/*
* 3. try reading the termcap
*/
if (Columns == 0 || Rows == 0)
getlinecol(); /* get "co" and "li" entries from termcap */
#endif
/*
* 4. If everything fails, use the old values
*/
if (Columns <= 0 || Rows <= 0)
{
Columns = old_Columns;
Rows = old_Rows;
return FAIL;
}
check_winsize();
/* if size changed: screenalloc will allocate new screen buffers */
return OK;
}
void
mch_set_winsize()
{
char_u string[10];
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_set_winsize();
return;
}
#endif
/* try to set the window size to Rows and Columns */
if (is_iris_ansi(term_strings[KS_NAME]))
{
sprintf((char *)string, "\033[203;%ld;%ld/y", Rows, Columns);
outstrn(string);
flushbuf();
screen_start(); /* don't know where cursor is now */
}
}
int
call_shell(cmd, options)
char_u *cmd;
int options; /* SHELL_FILTER if called by do_filter() */
/* SHELL_COOKED if term needs cooked mode */
/* SHELL_EXPAND if called by ExpandWildCards() */
{
#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
int x;
#ifndef __EMX__
char_u newcmd[1024]; /* only needed for unix */
#else /* __EMX__ */
/*
* Set the preferred shell in the EMXSHELL environment variable (but
* only if it is different from what is already in the environment).
* Emx then takes care of whether to use "/c" or "-c" in an
* intelligent way. Simply pass the whole thing to emx's system() call.
* Emx also starts an interactive shell if system() is passed an empty
* string.
*/
char_u *p, *old;
if (((old = getenv("EMXSHELL")) == NULL) || strcmp(old, p_sh))
{
/* should check HAVE_SETENV, but I know we don't have it. */
p = alloc(10 + strlen(p_sh));
if (p)
{
sprintf(p, "EMXSHELL=%s", p_sh);
putenv(p); /* don't free the pointer! */
}
}
#endif
flushbuf();
if (options & SHELL_COOKED)
settmode(0); /* set to cooked mode */
#ifdef __EMX__
if (cmd == NULL)
x = system(""); /* this starts an interactive shell in emx */
else
x = system(cmd);
if (x == -1) /* system() returns -1 when error occurs in starting shell */
{
MSG_OUTSTR("\nCannot execute shell ");
msg_outstr(p_sh);
msg_outchar('\n');
}
#else /* not __EMX__ */
if (cmd == NULL)
x = system(p_sh);
else
{
sprintf(newcmd, "%s %s -c \"%s\"", p_sh,
extra_shell_arg == NULL ? "" : (char *)extra_shell_arg,
(char *)cmd);
x = system(newcmd);
}
if (x == 127)
{
MSG_OUTSTR("\nCannot execute shell sh\n");
}
#endif /* __EMX__ */
else if (x && !expand_interactively)
{
msg_outchar('\n');
msg_outnum((long)x);
MSG_OUTSTR(" returned\n");
}
settmode(1); /* set to raw mode */
#ifdef OS2
/* external command may change the window size in OS/2, so check it */
mch_get_winsize();
#endif
resettitle();
return (x ? FAIL : OK);
#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */
#define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shell use that already */
char_u newcmd[1024];
int pid;
#ifdef HAVE_UNION_WAIT
union wait status;
#else
int status = -1;
#endif
int retval = FAIL;
char **argv = NULL;
int argc;
int i;
char_u *p;
int inquote;
#ifdef USE_GUI
int pty_master_fd = -1; /* for pty's */
int pty_slave_fd = -1;
char *tty_name;
int fd_toshell[2]; /* for pipes */
int fd_fromshell[2];
int pipe_error = FALSE;
# ifdef HAVE_SETENV
char envbuf[50];
# else
static char envbuf_Rows[20];
static char envbuf_Columns[20];
# endif
#endif
int did_settmode = FALSE; /* TRUE when settmode(1) called */
flushbuf();
if (options & SHELL_COOKED)
settmode(0); /* set to cooked mode */
/*
* 1: find number of arguments
* 2: separate them and built argv[]
*/
STRCPY(newcmd, p_sh);
for (i = 0; i < 2; ++i)
{
p = newcmd;
inquote = FALSE;
argc = 0;
for (;;)
{
if (i == 1)
argv[argc] = (char *)p;
++argc;
while (*p && (inquote || (*p != ' ' && *p != TAB)))
{
if (*p == '"')
inquote = !inquote;
++p;
}
if (*p == NUL)
break;
if (i == 1)
*p++ = NUL;
p = skipwhite(p);
}
if (i == 0)
{
argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
if (argv == NULL) /* out of memory */
goto error;
}
}
if (cmd != NULL)
{
if (extra_shell_arg != NULL)
argv[argc++] = (char *)extra_shell_arg;
argv[argc++] = "-c";
argv[argc++] = (char *)cmd;
}
argv[argc] = NULL;
#ifdef tower32
/*
* reap lost children (seems necessary on NCR Tower,
* although I don't have a clue why...) (Slootman)
*/
while (wait(&status) != 0 && errno != ECHILD)
; /* do it again, if necessary */
#endif
#ifdef USE_GUI
/*
* First try at using a pseudo-tty to get the stdin/stdout of the executed
* command into the current window for the GUI.
*/
if (gui.in_use && show_shell_mess)
{
/*
* Try to open a master pty.
* If this works, open the slave pty.
* If the slave can't be opened, close the master pty.
*/
if (p_guipty)
{
pty_master_fd = OpenPTY(&tty_name); /* open pty */
if (pty_master_fd >= 0 && ((pty_slave_fd =
open(tty_name, O_RDWR | O_EXTRA)) < 0))
{
close(pty_master_fd);
pty_master_fd = -1;
}
}
/*
* If opening a pty didn't work, try using pipes.
*/
if (pty_master_fd < 0)
{
pipe_error = (pipe(fd_toshell) < 0);
if (!pipe_error) /* pipe create OK */
{
pipe_error = (pipe(fd_fromshell) < 0);
if (pipe_error) /* pipe create failed */
{
close(fd_toshell[0]);
close(fd_toshell[1]);
}
}
if (pipe_error)
{
MSG_OUTSTR("\nCannot create pipes\n");
flushbuf();
}
}
}
if (!pipe_error) /* pty or pipe opened or not used */
#endif
{
if ((pid = fork()) == -1) /* maybe we should use vfork() */
{
MSG_OUTSTR("\nCannot fork\n");
#ifdef USE_GUI
if (gui.in_use && show_shell_mess)
{
if (pty_master_fd >= 0) /* close the pseudo tty */
{
close(pty_master_fd);
close(pty_slave_fd);
}
else /* close the pipes */
{
close(fd_toshell[0]);
close(fd_toshell[1]);
close(fd_fromshell[0]);
close(fd_fromshell[1]);
}
}
#endif
}
else if (pid == 0) /* child */
{
reset_signals(); /* handle signals normally */
if (!show_shell_mess)
{
int fd;
/*
* Don't want to show any message from the shell. Can't just
* close stdout and stderr though, because some systems will
* break if you try to write to them after that, so we must
* use dup() to replace them with something else -- webb
*/
fd = open("/dev/null", O_WRONLY | O_EXTRA);
fclose(stdout);
fclose(stderr);
/*
* If any of these open()'s and dup()'s fail, we just continue
* anyway. It's not fatal, and on most systems it will make
* no difference at all. On a few it will cause the execvp()
* to exit with a non-zero status even when the completion
* could be done, which is nothing too serious. If the open()
* or dup() failed we'd just do the same thing ourselves
* anyway -- webb
*/
if (fd >= 0)
{
/* To replace stdout (file descriptor 1) */
dup(fd);
/* To replace stderr (file descriptor 2) */
dup(fd);
/* Don't need this now that we've duplicated it */
close(fd);
}
}
#ifdef USE_GUI
else if (gui.in_use)
{
#ifdef HAVE_SETSID
(void)setsid();
#endif
#ifdef TIOCSCTTY
/* try to become controlling tty (probably doesn't work,
* unless run by root) */
ioctl(pty_slave_fd, TIOCSCTTY, (char *)NULL);
#endif
/* Simulate to have a dumb terminal (for now) */
#ifdef HAVE_SETENV
setenv("TERM", "dumb", 1);
sprintf((char *)envbuf, "%ld", Rows);
setenv("ROWS", (char *)envbuf, 1);
sprintf((char *)envbuf, "%ld", Columns);
setenv("COLUMNS", (char *)envbuf, 1);
#else
/*
* Putenv does not copy the string, it has to remain valid.
* Use a static array to avoid loosing allocated memory.
*/
putenv("TERM=dumb");
sprintf(envbuf_Rows, "ROWS=%ld", Rows);
putenv(envbuf_Rows);
sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
putenv(envbuf_Columns);
#endif
if (pty_master_fd >= 0)
{
close(pty_master_fd); /* close master side of pty */
/* set up stdin/stdout/stderr for the child */
close(0);
dup(pty_slave_fd);
close(1);
dup(pty_slave_fd);
close(2);
dup(pty_slave_fd);
close(pty_slave_fd); /* has been dupped, close it now */
}
else
{
/* set up stdin for the child */
close(fd_toshell[1]);
close(0);
dup(fd_toshell[0]);
close(fd_toshell[0]);
/* set up stdout for the child */
close(fd_fromshell[0]);
close(1);
dup(fd_fromshell[1]);
close(fd_fromshell[1]);
/* set up stderr for the child */
close(2);
dup(1);
}
}
#endif
/*
* There is no type cast for the argv, because the type may be
* different on different machines. This may cause a warning
* message with strict compilers, don't worry about it.
*/
execvp(argv[0], argv);
exit(EXEC_FAILED); /* exec failed, return failure code */
}
else /* parent */
{
/*
* While child is running, ignore terminating signals.
*/
catch_signals(SIG_IGN);
#ifdef USE_GUI
/*
* For the GUI we redirect stdin, stdout and stderr to our window.
*/
if (gui.in_use && show_shell_mess)
{
#define BUFLEN 100 /* length for buffer, pseudo tty limit is 128 */
char_u buffer[BUFLEN];
int len;
int p_more_save;
int old_State;
int read_count;
int c;
int toshell_fd;
int fromshell_fd;
if (pty_master_fd >= 0)
{
close(pty_slave_fd); /* close slave side of pty */
fromshell_fd = pty_master_fd;
toshell_fd = dup(pty_master_fd);
}
else
{
close(fd_toshell[0]);
close(fd_fromshell[1]);
toshell_fd = fd_toshell[1];
fromshell_fd = fd_fromshell[0];
}
/*
* Write to the child if there are typed characters.
* Read from the child if there are characters available.
* Repeat the reading a few times if more characters are
* available. Need to check for typed keys now and then, but
* not too often (delays when no chars are available).
* This loop is quit if no characters can be read from the pty
* (WaitForChar detected special condition), or there are no
* characters available and the child has exited.
* Only check if the child has exited when there is no more
* output. The child may exit before all the output has
* been printed.
*
* Currently this busy loops!
* This can probably dead-lock when the write blocks!
*/
p_more_save = p_more;
p_more = FALSE;
old_State = State;
State = EXTERNCMD; /* don't redraw at window resize */
for (;;)
{
/*
* Check if keys have been typed, write them to the child
* if there are any. Don't do this if we are expanding
* wild cards (would eat typeahead).
*/
if (!(options & SHELL_EXPAND) &&
(len = mch_inchar(buffer, BUFLEN - 1, 10)) != 0)
{
/*
* For pipes:
* Check for CTRL-C: sent interrupt signal to child.
* Check for CTRL-D: EOF, close pipe to child.
*/
if (len == 1 && (pty_master_fd < 0 || cmd != NULL))
{
#ifdef SIGINT
if (buffer[0] == Ctrl('C'))
{
/* Use both kill() and killpg(), in case one
* of the two fails */
kill(pid, SIGINT);
# ifdef HAVE_KILLPG
killpg(0, SIGINT);
# endif
}
#endif
if (pty_master_fd < 0 && toshell_fd >= 0 &&
buffer[0] == Ctrl('D'))
{
close(toshell_fd);
toshell_fd = -1;
}
}
/* replace K_BS by <BS> and K_DEL by <DEL> */
for (i = 0; i < len; ++i)
{
if (buffer[i] == CSI && len - i > 2)
{
c = TERMCAP2KEY(buffer[i + 1], buffer[i + 2]);
if (c == K_DEL || c == K_BS)
{
vim_memmove(buffer + i + 1, buffer + i + 3,
(size_t)(len - i - 2));
if (c == K_DEL)
buffer[i] = DEL;
else
buffer[i] = Ctrl('H');
len -= 2;
}
}
else if (buffer[i] == '\r')
buffer[i] = '\n';
}
/*
* For pipes: echo the typed characters.
* For a pty this does not seem to work.
*/
if (pty_master_fd < 0)
{
for (i = 0; i < len; ++i)
if (buffer[i] == '\n' || buffer[i] == '\b')
msg_outchar(buffer[i]);
else
msg_outtrans_len(buffer + i, 1);
windgoto(msg_row, msg_col);
flushbuf();
}
/*
* Write the characters to the child, unless EOF has
* been typed for pipes. Ignore errors.
*/
if (toshell_fd >= 0)
write(toshell_fd, (char *)buffer, (size_t)len);
}
/*
* Check if the child has any characters to be printed.
* Read them and write them to our window.
* Repeat this a few times as long as there is something
* to do, avoid the 10ms wait for mch_inchar().
* TODO: This should handle escape sequences.
*/
for (read_count = 0; read_count < 10 &&
RealWaitForChar(fromshell_fd, 10); ++read_count)
{
len = read(fromshell_fd, (char *)buffer,
(size_t)BUFLEN);
if (len == 0) /* end of file */
goto finished;
buffer[len] = NUL;
msg_outstr(buffer);
windgoto(msg_row, msg_col);
cursor_on();
flushbuf();
}
/*
* Check if the child still exists when we finished
* outputting all characters.
*/
if (read_count == 0 &&
#ifdef __NeXT__
wait4(pid, &status, WNOHANG, (struct rusage *) 0) &&
#else
waitpid(pid, &status, WNOHANG) &&
#endif
WIFEXITED(status))
break;
}
finished:
p_more = p_more_save;
State = old_State;
if (toshell_fd >= 0)
close(toshell_fd);
close(fromshell_fd);
}
#endif /* USE_GUI */
/*
* Wait until child has exited.
*/
#ifdef ECHILD
/* Don't stop waiting when a signal (e.g. SIGWINCH) is received. */
while (wait(&status) == -1 && errno != ECHILD)
;
#else
wait(&status);
#endif
/*
* Set to raw mode right now, otherwise a CTRL-C after
* catch_signals will kill Vim.
*/
settmode(1);
did_settmode = TRUE;
catch_signals(deathtrap);
/*
* Check the window size, in case it changed while executing the
* external command.
*/
mch_get_winsize();
if (WIFEXITED(status))
{
i = WEXITSTATUS(status);
if (i)
{
if (i == EXEC_FAILED)
{
MSG_OUTSTR("\nCannot execute shell ");
msg_outtrans(p_sh);
msg_outchar('\n');
}
else if (!expand_interactively)
{
msg_outchar('\n');
msg_outnum((long)i);
MSG_OUTSTR(" returned\n");
}
}
else
retval = OK;
}
else
MSG_OUTSTR("\nCommand terminated\n");
}
}
vim_free(argv);
error:
if (!did_settmode)
settmode(1); /* always set to raw mode */
resettitle();
return retval;
#endif /* USE_SYSTEM */
}
/*
* The input characters are buffered to be able to check for a CTRL-C.
* This should be done with signals, but I don't know how to do that in
* a portable way for a tty in RAW mode.
*/
/*
* Internal typeahead buffer. Includes extra space for long key code
* descriptions which would otherwise overflow. The buffer is considered full
* when only this extra space (or part of it) remains.
*/
#define INBUFLEN 250
static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
static int inbufcount = 0; /* number of chars in inbuf[] */
/*
* is_input_buf_full(), is_input_buf_empty(), add_to_input_buf(), and
* trash_input_buf() are functions for manipulating the input buffer. These
* are used by the gui_* calls when a GUI is used to handle keyboard input.
*
* NOTE: These functions will be identical in msdos.c etc, and should probably
* be taken out and put elsewhere, but at the moment inbuf is only local.
*/
int
is_input_buf_full()
{
return (inbufcount >= INBUFLEN);
}
int
is_input_buf_empty()
{
return (inbufcount == 0);
}
/* Add the given bytes to the input buffer */
void
add_to_input_buf(s, len)
char_u *s;
int len;
{
if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
return; /* Shouldn't ever happen! */
while (len--)
inbuf[inbufcount++] = *s++;
}
/* Remove everything from the input buffer. Called when ^C is found */
void
trash_input_buf()
{
inbufcount = 0;
}
static int
Read(buf, maxlen)
char_u *buf;
long maxlen;
{
if (inbufcount == 0) /* if the buffer is empty, fill it */
fill_inbuf(TRUE);
if (maxlen > inbufcount)
maxlen = inbufcount;
vim_memmove(buf, inbuf, (size_t)maxlen);
inbufcount -= maxlen;
if (inbufcount)
vim_memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
return (int)maxlen;
}
void
mch_breakcheck()
{
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_update();
return;
}
#endif /* USE_GUI */
/*
* Check for CTRL-C typed by reading all available characters.
* In cooked mode we should get SIGINT, no need to check.
*/
if (curr_tmode && RealWaitForChar(0, 0L)) /* if characters available */
fill_inbuf(FALSE);
}
static void
fill_inbuf(exit_on_error)
int exit_on_error;
{
int len;
int try;
#ifdef USE_GUI
if (gui.in_use)
{
gui_mch_update();
return;
}
#endif
if (is_input_buf_full())
return;
/*
* Fill_inbuf() is only called when we really need a character.
* If we can't get any, but there is some in the buffer, just return.
* If we can't get any, and there isn't any in the buffer, we give up and
* exit Vim.
*/
for (try = 0; try < 100; ++try)
{
len = read(0, (char *)inbuf + inbufcount,
(size_t)(INBUFLEN - inbufcount));
if (len > 0)
break;
if (!exit_on_error)
return;
}
if (len <= 0)
{
windgoto((int)Rows - 1, 0);
fprintf(stderr, "Vim: Error reading input, exiting...\n");
ml_sync_all(FALSE, TRUE); /* preserve all swap files */
getout(1);
}
while (len-- > 0)
{
/*
* if a CTRL-C was typed, remove it from the buffer and set got_int
*/
if (inbuf[inbufcount] == 3)
{
/* remove everything typed before the CTRL-C */
vim_memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
inbufcount = 0;
got_int = TRUE;
}
++inbufcount;
}
}
/*
* Wait "msec" msec until a character is available from the keyboard or from
* inbuf[]. msec == -1 will block forever.
* When a GUI is being used, this will never get called -- webb
*/
static int
WaitForChar(msec)
long msec;
{
if (inbufcount) /* something in inbuf[] */
return 1;
return RealWaitForChar(0, msec);
}
/*
* Wait "msec" msec until a character is available from file descriptor "fd".
* Time == -1 will block forever.
* When a GUI is being used, this will not be used for input -- webb
*/
static int
RealWaitForChar(fd, msec)
int fd;
long msec;
{
#ifndef HAVE_SELECT
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN;
return (poll(&fds, 1, (int)msec) > 0); /* is this correct when fd != 0?? */
#else
struct timeval tv;
fd_set rfds, efds;
# ifdef __EMX__
/* don't check for incoming chars if not in raw mode, because select()
* always returns TRUE then (in some version of emx.dll) */
if (curr_tmode == 0)
return 0;
# endif
if (msec >= 0)
{
tv.tv_sec = msec / 1000;
tv.tv_usec = (msec % 1000) * (1000000/1000);
}
/*
* Select on ready for reading and exceptional condition (end of file).
*/
FD_ZERO(&rfds); /* calls bzero() on a sun */
FD_ZERO(&efds);
FD_SET(fd, &rfds);
FD_SET(fd, &efds);
return (select(fd + 1, &rfds, NULL, &efds, (msec >= 0) ? &tv : NULL) > 0);
#endif
}
/*
* ExpandWildCards() - this code does wild-card pattern matching using the shell
*
* return OK for success, FAIL for error (you may lose some memory) and put
* an error message in *file.
*
* num_pat is number of input patterns
* pat is array of pointers to input patterns
* num_file is pointer to number of matched file names
* file is pointer to array of pointers to matched file names
* On Unix we do not check for files only yet
* list_notfound is ignored
*/
extern char *mktemp __ARGS((char *));
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifndef SEEK_END
# define SEEK_END 2
#endif
int
ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
int num_pat;
char_u **pat;
int *num_file;
char_u ***file;
int files_only;
int list_notfound;
{
int i;
size_t len;
char_u *p;
#ifdef __EMX__
# define EXPL_ALLOC_INC 16
char_u **expl_files;
size_t files_alloced, files_free;
*num_file = 0; /* default: no files found */
files_alloced = EXPL_ALLOC_INC; /* how much space is allocated */
files_free = EXPL_ALLOC_INC; /* how much space is not used */
*file = (char_u **) alloc(sizeof(char_u **) * files_alloced);
if (!*file)
{
emsg(e_outofmem);
return FAIL;
}
for (; num_pat > 0; num_pat--, pat++)
{
expl_files = NULL;
if (vim_strchr(*pat, '$') || vim_strchr(*pat, '~'))
{
/* expand environment var or home dir */
char_u *buf = alloc(1024);
if (!buf)
{
emsg(e_outofmem);
return FAIL;
}
expand_env(*pat, buf, 1024);
if (mch_has_wildcard(buf)) /* still wildcards in there? */
{
expl_files = (char_u **)_fnexplode(buf);
}
if (expl_files == NULL)
{
/*
* If no wildcard still remaining, simply add
* the pattern to the results.
* If wildcard did not match, add the pattern to
* the list of results anyway. This way, doing
* :n exist.c notexist*
* will at least edits exist.c and then say
* notexist* [new file]
*/
expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
expl_files[0] = strsave(buf);
expl_files[1] = NULL;
}
vim_free(buf);
}
else
{
expl_files = (char_u **)_fnexplode(*pat);
if (expl_files == NULL)
{
/* see above for explanation */
expl_files = (char_u **)alloc(sizeof(char_u **) * 2);
expl_files[0] = strsave(*pat);
expl_files[1] = NULL;
}
}
if (!expl_files)
{
/* Can't happen */
char_u msg[128];
sprintf(msg, "%s (unix.c:%d)", e_internal, __LINE__);
emsg(msg);
*file = (char_u **)"";
*num_file = 0;
return OK;
}
/*
* Count number of names resulting from expansion,
* At the same time add a backslash to the end of names that happen to be
* directories, and replace slashes with backslashes.
*/
for (i = 0; (p = expl_files[i]) != NULL; i++, (*num_file)++)
{
if (--files_free == 0)
{
/* need more room in table of pointers */
files_alloced += EXPL_ALLOC_INC;
*file = (char_u **) realloc(*file,
sizeof(char_u **) * files_alloced);
files_free = EXPL_ALLOC_INC;
}
slash_adjust(p);
if (mch_isdir(p))
{
len = strlen(p);
p = realloc(p, len + 2);
if (!p)
{
emsg(e_outofmem);
return FAIL;
}
(*file)[*num_file] = p;
p += len;
*p++ = '\\';
*p = 0;
}
else
{
(*file)[*num_file] = strsave(p);
}
}
_fnexplodefree(expl_files);
}
return OK;
#else /* __EMX__ */
int dir;
char_u tmpname[TMPNAMELEN];
char_u *command;
FILE *fd;
char_u *buffer;
int use_glob = FALSE;
*num_file = 0; /* default: no files found */
*file = (char_u **)"";
/*
* If there are no wildcards, just copy the names to allocated memory.
* Saves a lot of time, because we don't have to start a new shell.
*/
if (!have_wildcard(num_pat, pat))
{
*file = (char_u **)alloc(num_pat * sizeof(char_u *));
if (*file == NULL)
{
*file = (char_u **)"";
return FAIL;
}
for (i = 0; i < num_pat; i++)
(*file)[i] = strsave(pat[i]);
*num_file = num_pat;
return OK;
}
/*
* get a name for the temp file
*/
STRCPY(tmpname, TMPNAME2);
if (*mktemp((char *)tmpname) == NUL)
{
emsg(e_notmp);
return FAIL;
}
/*
* let the shell expand the patterns and write the result into the temp file
* If we use csh, glob will work better than echo.
*/
if ((len = STRLEN(p_sh)) >= 3 && STRCMP(p_sh + len - 3, "csh") == 0)
use_glob = TRUE;
len = TMPNAMELEN + 11;
for (i = 0; i < num_pat; ++i) /* count the length of the patterns */
len += STRLEN(pat[i]) + 3;
command = alloc(len);
if (command == NULL)
return FAIL;
if (use_glob)
STRCPY(command, "glob >"); /* build the shell command */
else
STRCPY(command, "echo >"); /* build the shell command */
STRCAT(command, tmpname);
for (i = 0; i < num_pat; ++i)
{
#ifdef USE_SYSTEM
STRCAT(command, " \""); /* need extra quotes because we */
STRCAT(command, pat[i]); /* start the shell twice */
STRCAT(command, "\"");
#else
STRCAT(command, " ");
STRCAT(command, pat[i]);
#endif
}
if (expand_interactively)
show_shell_mess = FALSE;
/*
* If we use -f then shell variables set in .cshrc won't get expanded.
* vi can do it, so we will too, but it is only necessary if there is a "$"
* in one of the patterns, otherwise we can still use the fast option.
*/
if (use_glob && !have_dollars(num_pat, pat)) /* Use csh fast option */
extra_shell_arg = (char_u *)"-f";
i = call_shell(command, SHELL_EXPAND); /* execute it */
extra_shell_arg = NULL;
show_shell_mess = TRUE;
vim_free(command);
if (i == FAIL) /* call_shell failed */
{
vim_remove(tmpname);
/*
* With interactive completion, the error message is not printed.
* However with USE_SYSTEM, I don't know how to turn off error messages
* from the shell, so screen may still get messed up -- webb.
*/
#ifndef USE_SYSTEM
if (!expand_interactively)
#endif
{
must_redraw = CLEAR; /* probably messed up screen */
msg_outchar('\n'); /* clear bottom line quickly */
cmdline_row = Rows - 1; /* continue on last line */
}
return FAIL;
}
/*
* read the names from the file into memory
*/
fd = fopen((char *)tmpname, "r");
if (fd == NULL)
{
emsg2(e_notopen, tmpname);
return FAIL;
}
fseek(fd, 0L, SEEK_END);
len = ftell(fd); /* get size of temp file */
fseek(fd, 0L, SEEK_SET);
buffer = alloc(len + 1);
if (buffer == NULL)
{
vim_remove(tmpname);
fclose(fd);
return FAIL;
}
i = fread((char *)buffer, 1, len, fd);
fclose(fd);
vim_remove(tmpname);
if (i != len)
{
emsg2(e_notread, tmpname);
vim_free(buffer);
return FAIL;
}
if (use_glob) /* file names are separated with NUL */
{
buffer[len] = NUL; /* make sure the buffers ends in NUL */
i = 0;
for (p = buffer; p < buffer + len; ++p)
if (*p == NUL) /* count entry */
++i;
if (len)
++i; /* count last entry */
}
else /* file names are separated with SPACE */
{
buffer[len] = '\n'; /* make sure the buffers ends in NL */
p = buffer;
for (i = 0; *p != '\n'; ++i) /* count number of entries */
{
while (*p != ' ' && *p != '\n') /* skip entry */
++p;
p = skipwhite(p); /* skip to next entry */
}
}
if (i == 0)
{
/*
* Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I".
* /bin/sh will happily expand it to nothing rather than returning an
* error; and hey, it's good to check anyway -- webb.
*/
vim_free(buffer);
*file = (char_u **)"";
return FAIL;
}
*num_file = i;
*file = (char_u **)alloc(sizeof(char_u *) * i);
if (*file == NULL)
{
vim_free(buffer);
*file = (char_u **)"";
return FAIL;
}
/*
* Isolate the individual file names.
*/
p = buffer;
for (i = 0; i < *num_file; ++i)
{
(*file)[i] = p;
if (use_glob)
{
while (*p && p < buffer + len) /* skip entry */
++p;
++p; /* skip NUL */
}
else
{
while (*p != ' ' && *p != '\n') /* skip entry */
++p;
if (*p == '\n') /* last entry */
*p = NUL;
else
{
*p++ = NUL;
p = skipwhite(p); /* skip to next entry */
}
}
}
/*
* Move the file names to allocated memory.
*/
for (i = 0; i < *num_file; ++i)
{
/* Require the files to exist. Helps when using /bin/sh */
if (expand_interactively)
{
struct stat st;
int j;
if (stat((char *)((*file)[i]), &st) < 0)
{
for (j = i; j + 1 < *num_file; ++j)
(*file)[j] = (*file)[j + 1];
--*num_file;
--i;
continue;
}
}
/* if file doesn't exist don't add '/' */
dir = (mch_isdir((*file)[i]));
p = alloc((unsigned)(STRLEN((*file)[i]) + 1 + dir));
if (p)
{
STRCPY(p, (*file)[i]);
if (dir)
STRCAT(p, "/");
}
(*file)[i] = p;
}
vim_free(buffer);
if (*num_file == 0) /* rejected all entries */
{
vim_free(*file);
*file = (char_u **)"";
return FAIL;
}
return OK;
#endif /* __EMX__ */
}
int
mch_has_wildcard(p)
char_u *p;
{
for ( ; *p; ++p)
{
if (*p == '\\' && p[1] != NUL)
++p;
else if (vim_strchr((char_u *)"*?[{`~$", *p) != NULL)
return TRUE;
}
return FALSE;
}
#ifndef __EMX__
static int
have_wildcard(num, file)
int num;
char_u **file;
{
register int i;
for (i = 0; i < num; i++)
if (mch_has_wildcard(file[i]))
return 1;
return 0;
}
static int
have_dollars(num, file)
int num;
char_u **file;
{
register int i;
for (i = 0; i < num; i++)
if (vim_strchr(file[i], '$') != NULL)
return TRUE;
return FALSE;
}
#endif /* ifndef __EMX__ */
#ifndef HAVE_RENAME
/*
* Scaled-down version of rename, which is missing in Xenix.
* This version can only move regular files and will fail if the
* destination exists.
*/
int
rename(src, dest)
const char *src, *dest;
{
struct stat st;
if (stat(dest, &st) >= 0) /* fail if destination exists */
return -1;
if (link(src, dest) != 0) /* link file to new name */
return -1;
if (vim_remove(src) == 0) /* delete link to old name */
return 0;
return -1;
}
#endif /* !HAVE_RENAME */